import logging
import os
import re
import time

import allure
import yaml
from jsonpath import jsonpath
from playwright.sync_api import Page, expect


class BasePage:

    def __init__(self, page: Page):
        self.page = page
        # 将logging赋值给self.logger
        self.logger = logging
        # 初始化locator为None
        self.locator = None
        # 登录账号在此配置

    def goto_url(self, url):
        """
        请求地址功能
        :param url: 传入指定页面地址
        """
        login_data = self.read_yaml_json("configs/config.yaml", "$..formal_login")
        result_url = login_data["cookies_url"] + url
        self.page.goto(result_url)
        print(f"访问地址: {result_url}")
        # 等待页面加载完毕
        self.page.wait_for_load_state('load')

        # 判断页面是否是登录状态
        if "login" in self.page.url:
            print("cookies无效,进入登录步骤")
            print(f"这个请求是{login_data['url']}")
            self.login(login_data['url'], login_data["username"], login_data["password"])
            # 等待登录成功
            self.page.wait_for_load_state('networkidle')
            # 获取cookies进行存入操作
            self.page.context.storage_state(path="configs/cookies.json")
            # 再次请求目标地址
            self.page.goto(result_url)
        else:
            print("cookies有效,跳过登录步骤")

    def login(self, url, username, password):
        # 传入登录前的Url
        self.page.goto(url)
        self.page.locator("input[maxlength='16']").fill(f"{username}")
        self.page.locator("input[maxlength='18']").fill(f"{password}")
        # 执行登录滑块操作
        drop_button = self.page.get_by_role("article").locator("form div").filter(
            has_text="请按住滑块拖动 登 录").locator("div").nth(4)
        box = drop_button.bounding_box()
        self.page.mouse.move(box['x'] + box['width'] / 2, box['y'] + box['height'] / 2)
        self.page.mouse.down()
        mov_x = box['x'] + box['width'] / 2 + 260
        self.page.mouse.move(mov_x, box['y'] + box['height'] / 2)
        self.page.mouse.up()
        self.page.locator("button[class*='mt']").click()

    def wait_for_page_load(self):
        """
        使用js等待页面上的所有资源（包括图片、样式表、脚本等）都已加载完成
        """
        self.page.wait_for_function("document.readyState === 'complete'")

    def read_yaml_json(self, path, json_path="null"):
        # 打开文件
        project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        full_yaml_path = os.path.join(project_root, path)
        with open(full_yaml_path, 'r', encoding="utf-8") as f:
            # 读取文件内容
            data = yaml.safe_load(f)
        # 如果json_path为null
        if json_path == "null":
            # 打印读取文件信息
            self.logger.info(f"读取文件名: [{path}] , 返回全部内容: [{data}]")
            # 返回文件内容
            return data
        else:
            # 使用jsonpath读取文件内容
            result = jsonpath(data, json_path)
            # 获取读取的内容
            cleaned_result = result[0]
            # 打印读取文件信息
            self.logger.info(f"读取文件名:[{path}] , 使用jsonpath读取[{json_path}]下的内容: [{cleaned_result}]")
            # 返回读取的内容
            return cleaned_result

    def read_yaml(self, path):
        """
         读取yaml文件内容
        :param path: 文件路径
        :return: 返回文件全部内容
        示例:  data[""]   data[""][""]
        """
        # 读取YAML文件
        with open(path, 'r') as f:
            data = yaml.safe_load(f)
        # 返回读取到的全部内容
        self.logger.info(f"读取的yaml文件内容为: {data}")
        return data

    def write_yaml(self, path, data):
        # 写入 YAML 文件
        with open(path, 'w') as f:
            yaml.safe_dump(data, f)
        self.logger.info(f"写入的yaml文件内容为: {data}")

    def click_module_max(self, module_name, com=0):
        # 大菜单栏
        pattern = "^" + module_name + "$"
        self.page.locator("div").filter(has_text=re.compile(pattern)).nth(com)

    def click_module_min(self, module_name, com=0):
        # 小菜单栏
        self.page.get_by_role("link", name=f"{module_name}").nth(com)

    def click_more_functions(self, module, functions_name):
        """
        封装页面三个点内的功能，比如参数读取xxx，那个ul/li列表
        Args:
            :param functions_name: 小标签
            :param module: 模块名称
        """
        time.sleep(1)
        try:
            # 尝试点击第一个按钮
            self.page.get_by_role("button", name="").click()
        except Exception as e:
            print(f"第一个按钮点击失败: {e}")
            self.page.get_by_role("row", name="   ", exact=True).get_by_role("button").click()
            # 如果第一个按钮点击失败，尝试点击第二个按钮
        # 点击小标签
        time.sleep(1)
        self.page.locator(f"li:has-text('{functions_name}')").last.click()

    def assert_alert_content(self, expected_text: str, timeout: int = 10000):
        """
        断言自定义弹窗内容（适用于 ARIA role="alert" 的元素）

        Args:
            expected_text (str): 预期的弹窗文本
            timeout (int): 等待弹窗出现的超时时间（毫秒）
        """
        try:
            # 定位弹窗元素并等待其可见
            alert_dialog = self.page.get_by_role("alert").first
            expect(alert_dialog).to_be_visible(timeout=timeout)  # 显式等待弹窗出现

            # 获取弹窗文本
            actual_text = alert_dialog.inner_text()

            # 断言内容
            assert actual_text == expected_text, \
                f"弹窗内容不匹配 | 预期: {expected_text}, 实际: {actual_text}"

            # 记录日志
            self.logger.info(f"弹窗断言成功: {expected_text}")

        except Exception as e:
            # 失败时附加截图到 Allure 报告
            self.logger.error(f"弹窗断言失败: {str(e)}")
            self._attach_screenshot_to_allure("断言失败截图")
            raise e

    def _attach_screenshot_to_allure(self, name: str):
        """附加截图到 Allure 报告"""
        allure.attach(
            self.page.screenshot(),
            name=name,
            attachment_type=allure.attachment_type.PNG
        )

    def list_row(self, row):
        """
        勾选row的行
        Args:
            row: 列表的行数 1 2 3  序号
        """
        self.page.get_by_role("row", name=f"{row}", exact=True).locator("label span").nth(1)

    def cat_url(self, locator, url):
        # 拦截指定接口
        self.locator = locator
        with self.page.expect_response(f"**/{url}/**") as response_info:
            self.locator.click()
            response = response_info.value  # 获取接口响应的结果。
            return response.json()
